package p.ithorns.tools.translator.apt.utils;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * UnsafeUtil
 *
 * @author Ht.L
 * @date 2024-05-03 15:59
 * @since 1.0.0
 */
public class UnsafeUtil {

    public static final String[] JAVAC_PKG_S = {
            "com.sun.tools.javac.code",
            "com.sun.tools.javac.comp",
            "com.sun.tools.javac.file",
            "com.sun.tools.javac.main",
            "com.sun.tools.javac.model",
            "com.sun.tools.javac.parser",
            "com.sun.tools.javac.processing",
            "com.sun.tools.javac.tree",
            "com.sun.tools.javac.util",
            "com.sun.tools.javac.jvm",
    };

    // private final static Logger log = LoggerFactory.getLogger(UnsafeUtil.class);

    /**
     * Unsafe mechanics
     */
    private static final Unsafe UNSAFE;

    static {
        try {
            UNSAFE = reflectGetUnsafe();
            assert UNSAFE != null;
        } catch (Exception e) {
            throw new Error(e);
        }
    }


    public static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * copy from lombok
     * <p>
     * call public api: ModuleLayer.boot().findModule("jdk.compiler").get();
     * but use reflection because we don't want this code to crash on jdk1.7 and below.
     * In that case, none of this stuff was needed in the first place, so we just exit via
     * the catch block and do nothing.
     *
     * see -LombokProcessor#getJdkCompilerModule
     */
    public static Object getJdkCompilerModule() {
        try {
            Class<?> cModuleLayer = Class.forName("java.lang.ModuleLayer");
            Method mBoot = cModuleLayer.getDeclaredMethod("boot");
            Object bootLayer = mBoot.invoke(null);
            Class<?> cOptional = Class.forName("java.util.Optional");
            Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class);
            Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler");
            return cOptional.getDeclaredMethod("get").invoke(oCompilerO);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return null;
        }
    }

    private static long getFirstFieldOffset() {
        try {
            return UNSAFE.objectFieldOffset(Parent.class.getDeclaredField("first"));
        } catch (NoSuchFieldException e) {
            // can't happen.
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            // can't happen
            throw new RuntimeException(e);
        }
    }

    public static void putBooleanVolatile(Object o, boolean value) {
        long firstFieldOffset = getFirstFieldOffset();
        UNSAFE.putBooleanVolatile(o, firstFieldOffset, value);
    }

    public static void replaceModule(Class<?> clazz) {
        try {
            Module module = Object.class.getModule();
            long addr = UNSAFE.objectFieldOffset(Class.class.getDeclaredField("module"));
            UNSAFE.getAndSetObject(clazz, addr, module);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    public static void putOrderedInt(Object obj, long stateOffset, int exceptional) {
        UNSAFE.putOrderedInt(obj, stateOffset, exceptional);
    }


    // 实现compareAndSwapInt方法
    public static boolean compareAndSwap(Object obj, long offset, int expect, int update) {
        return UNSAFE.compareAndSwapInt(obj, offset, expect, update);
    }

    public static boolean compareAndSwapObject(Object o, long offset,
                                               Object expected,
                                               Object x) {
        return UNSAFE.compareAndSwapObject(o, offset, expected, x);
    }

    public static long objectFieldOffset(Class<?> clazz, String filedName) {
        try {
            Field declaredField = clazz.getDeclaredField(filedName);
            return UNSAFE.objectFieldOffset(declaredField);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    public static List<Field> getAllFieldsList(final Class<?> cls) {
        assert null != cls;
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(allFields, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
    }

}